package com.modbus.tcp.client;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException;
import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;
import com.intelligt.modbus.jlibmodbus.master.ModbusMasterTCP;
import com.intelligt.modbus.jlibmodbus.msg.ModbusRequestBuilder;
import com.intelligt.modbus.jlibmodbus.msg.base.ModbusRequest;
import com.intelligt.modbus.jlibmodbus.msg.response.ReadHoldingRegistersResponse;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;


/**
 * ServerEndpoint
 * <p>
 * 使用springboot的唯一区别是要@Component声明下，而使用独立容器是由容器自己管理websocket的，但在springboot中连容器都是spring管理的。
 * <p>
 * 虽然@Component默认是单例模式的，但springboot还是会为每个websocket连接初始化一个bean，所以可以用一个静态set保存起来。
 *
 * @author sam
 * @since 2017/9/13
 */
@ServerEndpoint(value ="/webSocket/{slaveId}/{address}/{quantity}") //WebSocket客户端建立连接的地址
@Component
@Data
@Slf4j
public class WebsocketServerEndpoint{


    @Value("${modbus-tcp.host}")
    private String host;

    @Value("${modbus-tcp.port}")
    private int port;


    static ModbusMaster master;
    static ModbusMasterTCP masterTCP;

    static ModbusRequest request;

    @PostConstruct
    public void initModbusMaster() throws UnknownHostException {
        // 设置主机TCP参数
        TcpParameters tcpParameters = new TcpParameters();

        // 设置TCP的ip地址
        InetAddress adress = InetAddress.getByName(host);

        // TCP参数设置ip地址
        // tcpParameters.setHost(InetAddress.getLocalHost());
        tcpParameters.setHost(adress);

        // TCP设置长连接
        tcpParameters.setKeepAlive(true);
        // TCP设置端口，这里设置是默认端口502
        tcpParameters.setPort(port);

        // 创建一个主机
        master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
        Modbus.setAutoIncrementTransactionId(true);
    }

    /**
     * 建立连接的回调方法
     *
     * @param session 与客户端的WebSocket连接会话
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("slaveId") Integer slaveId, @PathParam("address") Integer address, @PathParam("quantity") Integer quantity) {
        try {

            try {
                if (!master.isConnected()) {
                    master.connect();// 开启连接
                }

                request = ModbusRequestBuilder.getInstance().buildReadHoldingRegisters(slaveId, address, quantity);

                sendMessage(session, "modbus 已连接");
            } catch (ModbusIOException e) {
                e.printStackTrace();
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 收到客户端消息的回调方法
     *
     * @param message 客户端传过来的消息
     * @param session 对应的session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        while (true) {
            ReadHoldingRegistersResponse response = null;
            try {
                response = (ReadHoldingRegistersResponse) master.processRequest(request);

                // 读取对应从机的数据，readInputRegisters读取的写寄存器，功能码04
                int[] registerValues = response.getRegisters();

                // 控制台输出
                for (int value : registerValues) {
                    sendMessage(session, value);
                }

                Thread.sleep(1000);
            } catch (ModbusProtocolException e) {
                e.printStackTrace();
            } catch (ModbusIOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }


    /**
     * 发生错误的回调方法
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 关闭连接的回调方法
     */
    @OnClose
    public void onClose(Session session) {
        try {
            master.disconnect();
        } catch (ModbusIOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 单独发送消息
     *
     * @param session
     * @param message
     */
    public void sendMessage(Session session, Object message) {
        try {

            session.getBasicRemote().sendObject(JSON.toJSONString(message));
        } catch (IOException | EncodeException e) {
            e.printStackTrace();
        }
    }
}
